package com.goaly.action;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.PathUtil;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.SystemPropsUtil;
import com.goaly.gpt.QwenTestCodeGenerator;
import com.goaly.gpt.TestCodeGenerator;
import com.goaly.gpt.XfsTestCodeGenerator;
import com.goaly.gpt.consts.GptConstant;
import com.goaly.util.VirtualFileUtil;
import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectUtil;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.PsiImportStatement;
import com.intellij.psi.PsiJavaFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiPackageStatement;
import com.intellij.psi.util.PsiTreeUtil;
import groovy.lang.Tuple2;
import java.nio.file.Paths;
import java.util.regex.Pattern;
import lombok.SneakyThrows;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * 第一个演示Action - 弹出对话框显示class文件和路径信息
 *
 * @author lys
 */
public class CodeHelperAction extends AnAction {

    /**
     * 正则表达式 - JAVA源码目录
     */
    private final static Pattern REG_EXP_SRC_JAVA = Pattern.compile("src/main");

    private TestCodeGenerator codeGenerator;

    private boolean hasApiKey = false;

    public CodeHelperAction() {

        this.codeGenerator = new QwenTestCodeGenerator();
        String userHome = SystemPropsUtil.get(GptConstant.USER_HOME, true);
        String pathDashScopeApiKey = StrUtil.format("{}/{}", userHome, GptConstant.PATH_DASH_SCOPE_API_KEY);
        String pathXfsChatGptApiKey = StrUtil.format("{}/{}", userHome, GptConstant.PATH_XFS_CHAT_GPT_API_KEY);
        if (PathUtil.exists(Paths.get(pathXfsChatGptApiKey), false)) {
            hasApiKey = true;
            String xfsChatGptApiKey = FileUtil.readUtf8String(pathXfsChatGptApiKey);
            this.codeGenerator = new XfsTestCodeGenerator(xfsChatGptApiKey);
        } else if (PathUtil.exists(Paths.get(pathDashScopeApiKey), false)) {
            hasApiKey = true;
        }
    }

    @SneakyThrows
    @Override
    public void actionPerformed(@NotNull AnActionEvent event) {
        hasApiKey = hasApiKey || checkHasApiKey();
        if (!hasApiKey) {
            Messages.showMessageDialog("请先配置GPT的api_key，", "提示", Messages.getInformationIcon());
            return;
        }

        Project project = event.getProject();
        PsiFile psiFile = event.getData(CommonDataKeys.PSI_FILE);
        // 获取当前文件
        PsiFile file = event.getData(CommonDataKeys.PSI_FILE);
        PsiJavaFile javaFile = checkAndGetPsiJavaFile(project, file);
        if (javaFile == null) {
            return;
        }
//        String classPath = psiFile.getVirtualFile().getPath();

        // 获取当前类
        PsiClass psiClass = PsiTreeUtil.getChildOfType(javaFile, PsiClass.class);

        // 获取当前光标
        Caret caret = event.getData(CommonDataKeys.CARET);
        int offset = caret.getOffset();
        PsiElement psiElement = javaFile.findElementAt(offset);
        PsiMethod psiMethod = PsiTreeUtil.getParentOfType(psiElement, PsiMethod.class);
        StringBuilder testCodeBuf = StrUtil.builder();
        if (psiMethod != null) {

            String className = psiClass.getName();
            String methodName = psiMethod.getName();
            // todo 添加method相关对象导入包
           /* PsiImportStatement[] importStatements = PsiTreeUtil.getChildrenOfType(psiClass, PsiImportStatement.class);
            if (importStatements != null) {
                for (PsiImportStatement importStatement : importStatements) {
                    String importText = importStatement.getText();
                    if (importText.contains("org.junit.jupiter.api.Test")) {
                        continue;
                    }
                    testCodeBuf.append(importText).append("\n");
                }
            }
            psiMethod.getBody().getOwnDeclarations()*/


            // 调用GPT接口生成测试代码
            try {
                Tuple2<String, String> tuple2 = codeGenerator.getCode(className, psiMethod.getText());
                String testCode = tuple2.getFirst();
                if (StrUtil.isBlank(testCode)) {
                    // gpt未生成代码，将返回内容添加成注方法注释
                    String resultText = StrUtil.replace(tuple2.getSecond(), "\n", "\n    ");
                    String promptMsg = "    /* TODO GPT给出以下提示，请参考提示自行实现单元测试\n    ";
                    if (StrUtil.isBlank(resultText)) {
                        promptMsg = "    /* TODO GPT未返回结果，请自行实现单元测试\n    ";
                    }
                    testCode = StrUtil.builder()
                            .append("import org.junit.jupiter.api.extension.ExtendWith;\n")
                            .append("import org.junit.jupiter.api.Test;\n")
                            .append("import org.springframework.test.context.junit.jupiter.SpringExtension;\n")
                            .append("\n")
                            .append("@ExtendWith(SpringExtension.class)\n")
                            .append(StrFormatter.format("public class {}Test {\n", className))
                            .append("@Test \n")
                            .append(StrFormatter.format("public void {}() {\n", methodName))
                            .append(promptMsg)
                            .append(resultText)
                            .append("\n    */")
                            .append("\n}\n}")
                            .toString();
                }

                testCode = fixedPackageStatement(psiClass, testCode);
                testCodeBuf.append(testCode);
            } catch (IllegalStateException e) {
                Messages.showMessageDialog(project, e.getMessage(),
                        "Code Helper", Messages.getInformationIcon());
            }

            // ======================================== 测试代码 Start ========================================
//            String testCode = "import org.junit.jupiter.api.Test;\n"
//                    + "import org.junit.runner.RunWith;\n"
//                    + "import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;\n"
//                    + "import org.springframework.test.context.junit4.SpringRunner;\n"
//                    + "\n"
//                    + "@RunWith(SpringRunner.class)\n"
//                    + "@WebMvcTest\n"
//                    + "public class QianwenFastDemoTest {\n"
//                    + "\n"
//                    + "    @Test\n"
//                    + "    public void testOnComplete() {\n"
//                    + "        QianwenFastDemo demo = new QianwenFastDemo();\n"
//                    + "        demo.onComplete();\n"
//                    + "        System.out.println(\"Done!\");\n"
//                    + "    }\n"
//                    + "}";
//            testCodeBuf.append(testCode);

//            String testResult = "这个测试类使用了SpringRunner来运行应用程序，并且在@RunWith注解中指定了它。"
//                    + "\n@WebMvcTest注解告诉JUnit使用Spring Boot提供的WebMvc来运行测试。\n"
//                    + "最后，testOnComplete()方法调用了QianwenFastDemo对象的方法onComplete()并打印了“Done!”。";
//            testResult = StrUtil.replace(testResult, "\n", "\n    ");
//            testCodeBuf
//                    .append("import org.junit.jupiter.api.extension.ExtendWith;\n")
//                    .append("import org.junit.jupiter.api.BeforeEach;\n")
//                    .append("import org.junit.jupiter.api.Test;\n")
//                    .append("import org.springframework.test.context.junit.jupiter.SpringExtension;\n")
//                    .append("\n")
//                    .append("@ExtendWith(SpringExtension.class)\n")
//                    .append(StrFormatter.format("public class {}Test {\n", className))
//                    .append("@BeforeEach \n")
//                    .append("public void setUp() {\n")
//                    .append(StrFormatter.format("    System.out.println(\"BeforeEach测试开始于 {}\");", DateUtil.now()))
//                    .append("\n}")
//                    .append("@Test \n")
//                    .append(StrFormatter.format("public void {}() {\n", methodName))
//                    .append("    /* TODO GPT给出以下提示，请参考提示自行实现单元测试\n    ")
//                    .append(testResult)
//                    .append("\n    */")
//                    .append("\n}\n}");
            // ======================================== 测试代码 End ========================================

        } else {
            Messages.showMessageDialog(project, "请在方法内部使用",
                    "Code Helper", Messages.getInformationIcon());
            return;
        }
        if (testCodeBuf.length() == 0) {
            Messages.showMessageDialog(project, "调用GPT接口生成测试代码失败，请重试。",
                    "Code Helper", Messages.getInformationIcon());
            return;
        }

        // 文件生成
        VirtualFile projectDir = ProjectUtil.guessProjectDir(project);
        String projectBaseDir = projectDir.getCanonicalPath();

        String relativePath4Test = StrUtil.removePrefix(psiFile.getParent().getVirtualFile().getCanonicalPath(),
                projectBaseDir);
        relativePath4Test = ReUtil.replaceFirst(REG_EXP_SRC_JAVA, relativePath4Test, "src/test");
        String testFileName = StrFormatter.format("{}Test", psiClass.getName());
        String testFileFullPath = StrFormatter.format("{}/{}.java", relativePath4Test, testFileName);

        VirtualFile testFile = projectDir.findFileByRelativePath(testFileFullPath);
        if (testFile == null) {
            // 测试文件不存在，创建文件

            Tuple2<VirtualFile, Boolean> tuple2 = VirtualFileUtil.createOrGetVirtualFile(projectDir,
                    testFileFullPath, testCodeBuf.toString());
            testFile = tuple2.getFirst();
        } else {
            // 测试文件已存在，插入代码
            // 生成临时文件
            String tempTestFileName = StrFormatter.format("{}_{}", testFileName, System.currentTimeMillis());
            PsiFileFactory psiFileFactory = PsiFileFactory.getInstance(project);
            PsiFile tempPsiFile = psiFileFactory.createFileFromText(tempTestFileName, JavaFileType.INSTANCE,
                    testCodeBuf.toString());
            // 合并代码
            VirtualFileUtil.mergeJavaCode(project, testFile, tempPsiFile);

        }

        if (testFile.isValid()) {
            Messages.showMessageDialog(project, StrUtil.format("{}.{}单元测试已生成！",
                            psiMethod.getContainingClass().getName(),
                            psiMethod.getName()),
                    "Code Helper", Messages.getInformationIcon());
        }

    }

    /**
     * 检查Java文件是否可测试
     *
     * @param project 项目
     * @param file    文件
     * @return Java文件
     */
    @Nullable
    private PsiJavaFile checkAndGetPsiJavaFile(Project project, PsiFile file) {
        if (!JavaFileType.INSTANCE.equals(file.getFileType())) {
            Messages.showMessageDialog(project, "请在在Java文件中使用生成单元测试功能。",
                    "Code Helper", Messages.getInformationIcon());
            return null;
        }
        PsiJavaFile javaFile = (PsiJavaFile) file;
        // 检查Java文件是否可测试
        String errorMessage = checkTestable(javaFile);
        if (StrUtil.isNotBlank(errorMessage)) {
            Messages.showMessageDialog(project, errorMessage,
                    "Code Helper", Messages.getInformationIcon());
            return null;
        }
        return javaFile;
    }

    /**
     * 检查Java文件是否可测试
     *
     * @param javaFile Java文件
     * @return 错误信息
     */
    private String checkTestable(PsiJavaFile javaFile) {
        StringBuilder errMsgBuf = StrUtil.builder();
        PsiImportStatement[] importStatements = javaFile.getImportList().getImportStatements();
        boolean hasTestImport = false;
        boolean hasExtendWith = false;
        boolean hasSpringBootTest = false;
        for (PsiImportStatement importStatement : importStatements) {
            String importText = importStatement.getText();
            if (importText.contains("org.junit.jupiter.api.Test") || importText.contains("org.junit.Test")) {
                hasTestImport = true;
                break;
            }
            if (importText.contains("org.junit.jupiter.api.extension.ExtendWith")) {
                hasExtendWith = true;
                break;
            }
            if (importText.contains("org.springframework.boot.test.context.SpringBootTest")) {
                hasSpringBootTest = true;
                break;
            }
        }
        if (hasTestImport || hasExtendWith || hasSpringBootTest) {
            errMsgBuf.append("当前文件已经是单元测试文件，无需再次生成。");
        }
        return errMsgBuf.toString();
    }

    /**
     * 修复包名
     *
     * @param psiClass       待测试类
     * @param testCodeByQWen 单元测试代码
     * @return 修复后的单元测试代码
     */
    private static String fixedPackageStatement(PsiClass psiClass, String testCodeByQWen) {

        if (StrUtil.isBlank(testCodeByQWen)) {
            return testCodeByQWen;
        }
        PsiPackageStatement psiPackageStatement = PsiTreeUtil.getChildOfType(psiClass,
                PsiPackageStatement.class);
        if (psiPackageStatement != null) {
            String packageName = psiPackageStatement.getPackageName();
            String packageStatement = StrFormatter.format("package {};", packageName);
            if (!testCodeByQWen.contains(packageStatement)) {
                // 如果代码中没有包名，添加包名
                testCodeByQWen = StrFormatter.format("{}\n\n{}", packageStatement, testCodeByQWen);
            }
        }
        return testCodeByQWen;
    }

    /**
     * 检查是否有API Key
     * @return 是否有API Key
     */
    private boolean checkHasApiKey() {
        String userHome = SystemPropsUtil.get("user.home", true);
        String pathDashScopeApiKey = StrUtil.format("{}/{}", userHome, GptConstant.PATH_DASH_SCOPE_API_KEY);
        String pathXfsChatGptApiKey = StrUtil.format("{}/{}", userHome, GptConstant.PATH_XFS_CHAT_GPT_API_KEY);
        return PathUtil.exists(Paths.get(pathXfsChatGptApiKey), false) || PathUtil.exists(
                Paths.get(pathDashScopeApiKey), false);
    }

}
